`timescale 1ns/1ps
`include "define.sv"

module tb_C_fifo_to_array;

    reg clk;
    reg reset;
    reg [1:0] matrix_type;
    reg [31:0] data_in_c;
    reg data_valid_in_c;
    wire [31:0] data_out_c [0:15][0:15];
    wire wr_en_c [0:15][0:15];
    reg array_ready;
    wire done;

    // 实例化待测模块
    C_fifo_to_array uut (
        .clk(clk),
        .reset(reset),
        .matrix_type(matrix_type),
        .data_in_c(data_in_c),
        .data_valid_in_c(data_valid_in_c),
        .data_out_c(data_out_c),
        .wr_en_c(wr_en_c),
        .array_ready(array_ready),
        .done(done)
    );

    // 时钟生成
    initial clk = 0;
    always #5 clk = ~clk; // 100MHz

    // 复位
    initial begin
        reset = 1;
        array_ready = 0;
        data_valid_in_c = 0;
        data_in_c = 0;
        matrix_type = `m16n16k16;
        #20;
        reset = 0;
        #10;
        array_ready = 1;
    end

    // 参考数据存储
    reg [31:0] ref_data [0:255];

    // 数据输入任务
    task send_matrix(input [1:0] mat_type, input int total_count);
        integer i;
        begin
            matrix_type = mat_type;
            data_valid_in_c = 1; // valid信号持续拉高
            for (i = 0; i < total_count; i = i + 1) begin
                @(negedge clk);
                data_in_c = i;
                ref_data[i] = i; // 记录输入
            end
            @(negedge clk);
            data_valid_in_c = 0; // 传输结束后拉低
        end
    endtask

    // m16n16k16 校验
    task check_m16n16k16;
        integer i, j, idx, error_cnt;
        begin
            error_cnt = 0;
            for (i = 0; i < 16; i = i + 1) begin
                for (j = 0; j < 16; j = j + 1) begin
                    idx = i * 16 + j;
                    if (data_out_c[i][j] !== ref_data[idx]) begin
                        $display("ERROR: m16n16k16 data_out_c[%0d][%0d]=%h, expect=%h", i, j, data_out_c[i][j], ref_data[idx]);
                        error_cnt = error_cnt + 1;
                    end
                end
            end
            if (error_cnt == 0)
                $display("m16n16k16 数据校验通过！");
            else
                $display("m16n16k16 数据校验失败，错误数：%0d", error_cnt);
        end
    endtask

    // m8n32k16 校验
    task check_m8n32k16;
        integer i, j, idx, error_cnt;
        begin
            error_cnt = 0;
            for (i = 0; i < 8; i = i + 1) begin
                for (j = 0; j < 32; j = j + 1) begin
                    if (j < 16)
                        idx = i * 16 + j;
                    else
                        idx = (i + 8) * 16 + (j - 16);
                    if (data_out_c[i][j] !== ref_data[idx]) begin
                        $display("ERROR: m8n32k16 data_out_c[%0d][%0d]=%h, expect=%h", i, j, data_out_c[i][j], ref_data[idx]);
                        error_cnt = error_cnt + 1;
                    end
                end
            end
            if (error_cnt == 0)
                $display("m8n32k16 数据校验通过！");
            else
                $display("m8n32k16 数据校验失败，错误数：%0d", error_cnt);
        end
    endtask

    // m32n8k16 校验
    task check_m32n8k16;
        integer i, j, idx, error_cnt;
        begin
            error_cnt = 0;
            for (i = 0; i < 32; i = i + 1) begin
                for (j = 0; j < 8; j = j + 1) begin
                    if (i < 16)
                        idx = i * 8 + j;
                    else
                        idx = (i - 16) * 8 + (j + 8);
                    if (data_out_c[i][j] !== ref_data[idx]) begin
                        $display("ERROR: m32n8k16 data_out_c[%0d][%0d]=%h, expect=%h", i, j, data_out_c[i][j], ref_data[idx]);
                        error_cnt = error_cnt + 1;
                    end
                end
            end
            if (error_cnt == 0)
                $display("m32n8k16 数据校验通过！");
            else
                $display("m32n8k16 数据校验失败，错误数：%0d", error_cnt);
        end
    endtask

    // 主测试流程
    initial begin
        // 等待复位结束
        @(negedge reset);

        // 测试 m16n16k16
        $display("Test m16n16k16...");
        send_matrix(`m16n16k16, 256);
        wait(done);
        $display("Done for m16n16k16!");
        check_m16n16k16;

        // 测试 m8n32k16
        reset = 1; @(negedge clk); reset = 0; @(negedge clk);
        $display("Test m8n32k16...");
        send_matrix(`m8n32k16, 256);
        wait(done);
        $display("Done for m8n32k16!");
        check_m8n32k16;

        // 测试 m32n8k16
        reset = 1; @(negedge clk); reset = 0; @(negedge clk);
        $display("Test m32n8k16...");
        send_matrix(`m32n8k16, 256);
        wait(done);
        $display("Done for m32n8k16!");
        check_m32n8k16;

        $display("All test finished!");
        $stop;
    end

endmodule